Skip to content

refactor(shader): migrate GLSL shaders to ShaderLab and clean up shader infrastructure #2961

Open
zhuxudong wants to merge 137 commits intogalacean:dev/2.0from
zhuxudong:refactor/glsl-to-shaderlab
Open

refactor(shader): migrate GLSL shaders to ShaderLab and clean up shader infrastructure #2961
zhuxudong wants to merge 137 commits intogalacean:dev/2.0from
zhuxudong:refactor/glsl-to-shaderlab

Conversation

@zhuxudong
Copy link
Copy Markdown
Member

@zhuxudong zhuxudong commented Apr 13, 2026

Summary

  • Migrate all built-in shaders from core/shaderlib (raw GLSL + rollup-plugin-glsl) to shader package as ShaderLab .shader files
  • Reorganize shader package into Shaders/ (ShaderLab entry points) and ShaderLibrary/ (GLSL include fragments)
  • Unify ShadowCaster/DepthOnly as shared Utility passes; PBR/BlinnPhong/Unlit/PBRSpecular use UsePass to reference them
  • Remove ShaderChunkLoader and shader path/chunk loading infrastructure (_shaderRootPath, basePathForIncludeKey)
  • Fix _resolveUsePass to support shader names containing "/" (e.g. Utility/ShadowMap)
  • Restore original FXAA3_11.glsl without logic modifications
  • Refactor render state to per-property 3-tier priority (shader constant > shaderData > material.renderState fallback)

Motivation

Raw GLSL shaders scattered across core/shaderlib relied on rollup-plugin-glsl for bundling and lacked the render state / pass structure that ShaderLab provides. This migration:

  1. Consolidates all shader source into the shader package with a clear Shaders + ShaderLibrary layout
  2. Enables ShaderLab features (render state declarations, UsePass, Editor properties) for built-in shaders
  3. Removes the legacy shader chunk loading path — the engine now exclusively uses registerIncludes() for include registration and ShaderLab for shader creation

Key Changes

Shader Migration (packages/shader/)

  • Shaders/: PBR, PBRSpecular, BlinnPhong, Unlit, Sprite, SpriteMask, Text, Trail, UIDefault, Skybox, SkyProcedural, BackgroundTexture, Particle, Blit, BlitScreen, ShadowMap, DepthOnly, PostProcess shaders (Bloom, SAO, FinalSRGB, FinalAntiAliasing, UberShader)
  • ShaderLibrary/: All .glsl include fragments organized by category (Common, Lighting, PBR, Skin, Shadow, Fog, PostProcess, Particle)

UsePass Architecture

  • Utility/ShadowMap and Utility/DepthOnly are canonical shared passes
  • Material shaders reference them via UsePass "Utility/ShadowMap/Default/ShadowCaster"
  • Registration order in registerShaders(): Utility first, then material shaders
  • _resolveUsePass now throws on malformed names or missing referenced shaders (was silently returning undefined)

Render State Per-Property Priority

  • New 3-tier priority: shader constant > shaderData value > material.renderState fallback
  • Added _constantPropertyMask (per-property bitmask) on ShaderPass
  • Removed _mergeUnmanagedFrom, _managedGroupMask, RenderStateGroupFlag, _copyFrom
  • Single RenderState._resolveValue helper handles bool / numeric / enum priority resolution

Infrastructure Cleanup

  • Delete ShaderChunkLoader, remove _shaderRootPath, basePathForIncludeKey from Preprocessor/ShaderLab/IShaderLab
  • Delete TransformFeedbackShader (replaced by ShaderPass with _feedbackVaryings)
  • Shader.create() only accepts ShaderLab source (vertex/fragment string overload kept as signature for tests)
  • Remove engine-toolkit dependency from e2e (WireframeManager, OrbitControl)

Breaking Changes

Built-in shader names renamed (no aliases — alpha stage)

Old name New name
pbr PBR
pbr-specular PBRSpecular
blinn-phong BlinnPhong
unlit Unlit
Sprite 2D/Sprite
SpriteMask 2D/SpriteMask
Text 2D/Text
Trail 2D/Trail
UIDefault 2D/UIDefault
skybox Sky/Skybox
SkyProcedural Sky/SkyProcedural
background-texture Sky/BackgroundTexture
particle-shader Particle
blit Utility/Blit
blit-screen Utility/BlitScreen
shadow-map Utility/ShadowMap
depth-only Utility/DepthOnly

Shader include paths reorganized

Old include New include
<common> Common/Common.glsl
<transform> Common/Transform.glsl
<fog> Common/Fog.glsl
<skin> Skin/Skin.glsl
<blendShape> Skin/BlendShape.glsl

API removals

  • setIsTransparent(value, passIndex?) / setBlendMode(value, passIndex?) / setRenderFace(value, passIndex?)passIndex parameter removed. For per-pass render state, declare it directly in ShaderLab RenderState block.
  • ShaderLib named exports (ShaderLib.common, ShaderLib.pbr_helper, etc.) — use Shader.find() for shader sources instead.
  • TransformFeedbackShader class removed (use ShaderPass with _feedbackVaryings).
  • ShaderChunkLoader, _shaderRootPath, basePathForIncludeKey removed.

Test Plan

  • npm run build passes (CI green)
  • npx vitest run — all unit tests pass, including:
    • tests/src/shader-lab/Precompile.test.ts — 92/92 pass
    • tests/src/core/shader/state/RenderState.test.ts — 18/18 pass (new, covers 3-tier priority)
    • tests/src/core/material/BaseMaterial.test.ts — 9/9 pass (extended)
  • E2E visual regression — all 4 shards green
  • ShaderLab parsing: PBR UsePass resolves correctly, render states preserved
  • Precompile benchmark: _createFromPrecompiled round-trip works for PBR
  • No runtime regression: PBR / Unlit / BlinnPhong / Sprite / Particle e2e cases passing

Summary by CodeRabbit

  • New Features

    • Replaced the old shader system with a new ShaderCompiler and added many namespaced built-in shaders (PBR, Unlit, BlinnPhong, Sky, Particle, PostProcess/Uber, Utility/Blit, etc.).
  • Documentation

    • Renamed docs to “Galacean Shader”, updated example/playground links and shader include paths to the new directory structure.

… and clean up old files

All GLSL chunks and complete shaders have been migrated from
packages/core/src/shaderlib/ to packages/shader/src/shaders/ with a
unified directory structure. ShaderLib.ts is now an empty runtime
registry populated by the shader package's registerIncludes(). Old
VS/FS shader pairs in extra/ are replaced by ShaderLab .shader files.
Post-process and AO GLSL includes updated to use new .glsl-suffixed
keys. Blit.vs.glsl relocated from extra/ to shaderlib root.
… assertions

Move camera_ProjectionParams declaration from Transform.glsl to
Common.glsl where it is actually used by remapDepthBufferEyeDepth().
Update ShaderLab test to match new PBR.shader structure with inlined
ShadowCaster/DepthOnly passes. Fix PrecompileABTest macro expansion
test to find Forward Pass by name instead of hardcoded index.
…te PostProcess/AO

Split shader package into two layers, create .shader files for
PostProcess and AO, simplify FXAA3_11.glsl for ShaderLab
compatibility, remove Shader.create() from core passes.
…ng with pre-migration source

FXAA3_11.glsl was incorrectly stripped of conditional branches
(FXAA_DISCARD, FXAA_FAST_PIXEL_OFFSET, FXAA_GATHER4_ALPHA) and type
alias macros during the initial migration. Restore the full original
1028-line version to preserve all code paths.

FinalAntiAliasing.glsl now includes the FXAA_GLSL_130/120 conditional
defines and uses FxaaFloat type aliases, matching the pre-migration
FinalAntiAliasing.fs.glsl source exactly.
…ly across all material shaders

PBR/BlinnPhong/Unlit/PBRSpecular now reference Utility/ShadowMap and
Utility/DepthOnly via UsePass instead of inlining or referencing PBR.
Fix _resolveUsePass to handle shader names containing "/" by parsing
from the end. Reorder registerShaders() so Utility shaders are created
before material shaders.
- Remove `path` parameter from `Shader.create()` ShaderLab overload
- Remove `_shaderRootPath` from ShaderPass
- Remove `basePathForIncludeKey` from IShaderLab, ShaderLab, and Preprocessor
- Remove relative path resolution in Preprocessor._replace()
- Delete ShaderChunkLoader and simplify ShaderLoader
- Clean up basePath references in tests and devtools example
@zhuxudong zhuxudong self-assigned this Apr 13, 2026
@zhuxudong zhuxudong added the shader Shader related functions label Apr 13, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Replaces the ShaderLab pipeline with a new ShaderCompiler and precompiled .gsp shader assets, renames and reorganizes shader include paths, updates engine/material/render pipeline to use shader-driven render states and constant-property masks, adjusts examples/E2E/tests to use ShaderCompiler, and adds bundler/CLI precompile tooling.

Changes

Cohort / File(s) Summary
Documentation & Examples
docs/**, docs/*/graphics/material/examples/*, examples/src/*.ts, examples/vite.config.js, examples/package.json
Terminology changed from "ShaderLab" → "Galacean Shader"/"ShaderCompiler"; example routes renamed shaderlab-*shader-*; many GLSL include paths updated to namespaced locations; examples switched to instantiate/use ShaderCompiler.
New Shader Compiler Package & Tooling
packages/shader-compiler/**, packages/shader-compiler/src/bundler/**, packages/shader-compiler/rollup.config.js, packages/shader-compiler/package.json
Adds @galacean/engine-shader-compiler with lexer/parser/semantic/codegen, comment-stripping utilities, bundler plugin, precompile/watch/index generator, and CLI (shader-compiler-precompile).
Precompiled Shader Assets (.gsp)
packages/shader/libs/**/*.gsp, packages/shader/libs/index.ts
Adds many precompiled shader assets (PBR, BlinnPhong, Particle, PostProcess, Utility, Sky, 2D, AO, etc.) and an index exporting their sources.
Core Shader Infrastructure
packages/core/src/shader/Shader.ts, ShaderPass.ts, ShaderPool.ts, shaderlib/*, ShaderFactory.ts, global.d.ts
Migrates Shader.create path to use ShaderCompiler/precompiled assets, adds ShaderPass constant-property mask and feedback varyings, replaces static ShaderLib with runtime include registration, and introduces ShaderPool.registerShaders to load precompiled shaders.
Render State & State Resolution
packages/core/src/shader/state/*.ts, RenderState.ts
Refactors render-state application to resolve values via a new _resolveValue flow using constant-property masks and per-material render-state fallbacks; many state _applyShaderDataValue signatures updated.
Materials, Pipeline & Rendering
packages/core/src/material/*.ts, RenderPipeline/*.ts, Blitter.ts, RenderQueue.ts, Background.ts, BasicResources.ts, postProcess/*, sky/*, lighting/ambientOcclusion/*
Materials now use namespaced shader names (e.g., PBR, Unlit), write render-state via shaderData uniforms instead of mutating pass renderStates, pipeline passes constant masks through state application, and several inline shader registrations removed.
Transform Feedback / Particles
packages/core/src/graphic/TransformFeedbackShader.ts (deleted), TransformFeedbackSimulator.ts, particle/*
Removes TransformFeedbackShader class; transform-feedback driven by ShaderPass with configured feedback varyings; particle feedback pass bound from precompiled assets.
Loader & Design API Changes
packages/loader/src/ShaderLoader.ts, packages/loader/src/index.ts, packages/design/src/**
Removes ShaderChunkLoader; ShaderLoader simplified (creates Shader from code only); design types renamed IShaderLabIShaderCompiler and signatures adjusted.
E2E Tests & Tooling Adjustments
e2e/**, e2e/.dev/vite.config.js, e2e/package.json, e2e/config.ts
Replaces ShaderLab with ShaderCompiler across tests, updates vite/e2e deps, removes OrbitControl usages in many cases replacing them with transform.lookAt, removes some debug wireframes, and deletes a large shaderlab PBR E2E case.
Build & Package Scripts
package.json, packages/shader/package.json, packages/shader-lab/package.json, packages/core/package.json
Adds root precompile script, wires precompile into build scripts, introduces shader-compiler workspace package, updates shader package exports, and adds shader-lab bundler/CLI entries.
Miscellaneous
packages/shader-lab/src/ShaderLabUtils.ts, packages/shader-compiler/src/**, packages/core/src/Engine.ts
Introduces comment-skip/remove utilities, swaps many internal imports from ShaderLab → ShaderCompiler, Engine config option renamed shaderLabshaderCompiler, and ShaderPool/Engine registration order adjusted to pre-register shaders.

Sequence Diagram(s)

sequenceDiagram
  participant App as App / Examples
  participant Engine as WebGLEngine
  participant Compiler as ShaderCompiler
  participant Pool as ShaderPool
  participant GPU as GPU/ShaderProgram

  App->>Engine: create({ shaderCompiler: new ShaderCompiler() })
  Engine->>Compiler: set as global compiler
  Compiler->>Pool: provide precompiled .gsp assets (register via precompile or runtime)
  Pool->>Engine: ShaderPool.registerShaders() (creates Shader/ShaderPass entries)
  App->>Engine: load material / Shader.find("PBR")
  Engine->>Pool: lookup Shader by name
  Engine->>GPU: request program via ShaderPass._getShaderProgram(macroCollection)
  GPU-->>Engine: compiled/linked ShaderProgram
  Engine->>GPU: render draw calls (using shaderData + constant-property masks)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • GuoLei1990
  • cptbtptpbcptdtptp
  • Sway007

"🐰
I hopped through code to shift the light,
From labs to compiled shards so bright.
Namespaced leaves and precompiled seeds,
The compiler hums — the engine speeds.
Celebrate the hop, a shader flight!"

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

…alacean#2960

- Delete unused noise files (NoiseCellular, NoisePerlin, NoisePsrd, NoiseSimplex)
- Add NoiseSimplexGrad.glsl with simplexGrad() returning vec3 gradient
- Add algorithm attribution comments to NoiseCommon.glsl
- Add Particle/Module/NoiseModule.glsl with curl noise sampling
- Integrate noise velocity into ParticleFeedback.glsl
- Update Shaders/index.ts registrations for new includes
- Remove _createShader wrapper that handled ShaderPool name conflicts
- ShaderPool no longer registers VS/FS fallbacks, so no conflict exists
- Fix Shaders/index.ts noise registrations reverted by case issue
…m core

- Strip shader package to zero dependencies (pure data exports only)
- Move include registration to ShaderPool.init() and shader registration
  to ShaderPool.registerShaders() in core
- Split exports: ShaderLibrary/index.ts for fragmentList,
  Shaders/index.ts for complete shader sources
- Auto-register built-in shaders in Engine._initialize() after ShaderLab
  is set, so external callers no longer need manual register calls
- Remove registerIncludes/registerShaders from all tests, examples, e2e
- Update docs to reflect automatic registration
… DepthOnly passes

- ShadowMap.shader: bind RenderQueueType = material_ShadowCasterRenderQueue
- DepthOnly.shader: bind RenderQueueType = material_DepthOnlyRenderQueue
- Remove duplicate lowercase shaders/utility/ entries from git index
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Apr 13, 2026
- rollup-plugin-shaderlab transforms .shader to IPrecompiledShader
  JSON when PRECOMPILE=true
- ShaderPool.registerShaders() detects string vs precompiled object
  and calls appropriate Shader factory method
- Move ShaderLab setting and registerShaders() from
  Engine._initialize() to constructor, before BasicResources
- Route .shader through shaderlabPlugin in rollup.config.js
- Tests read .shader source via readFile() instead of package import
- Remove redundant Shader.create(PBRSource) calls from e2e cases
…ransform

- Remove double shadow intensity mixing in sampleShadowMap, move fade calc back inside the if block
- Restore inverse-transpose normal transform for BlinnPhong skinning
… add sources subpath

- Add libs/ directory with 21 precompiled .gsp files (checked into git)
- libs/index.ts aggregates all .gsp imports as IPrecompiledShader objects
- src/index.ts re-exports from libs/ (FinalAntiAliasing kept as string fallback)
- Add src/sources.ts for editor use: raw .shader strings via /sources subpath
- Add package.json exports field with "." and "./sources" subpaths
- Add *.gsp type declaration in global.d.ts
- rollup-plugin-glsl: handle .gsp files (JSON → object export)
- rollup-plugin-shaderlab: revert to .gs only, add buildStart/watchChange hooks
- rollup.config.js: add sources build entry for shader package
- scripts/precompile-shaders.mjs: full/incremental precompile script
- ShaderPool: add missing UIDefaultSource registration
- Revert lazy getter in BasicResources, Background,
  PostProcessUberPass back to eager constructor init
- Migrate render states from TS to .shader files:
  * Blit/BlitScreen: DepthState disabled
  * BackgroundTexture: DepthState CompareFunction LessEqual
  * Sprite/Text: BlendState, DepthState, RasterState
- Remove unused imports
macOS case-insensitive FS caused both Shaders/ and shaders/
paths to coexist in the git index after directory rename.
…ile compatibility

- Sprite/Text shaders: use PBR-consistent variable names for render states
  (renderQueueType, sourceColorBlendFactor, blendEnabled, etc.)
- Skybox/SkyProcedural shaders: add hardcoded DepthState and RasterState
- BasicResources._create2DMaterial: use shaderData.setInt() instead of renderState API
- BaseMaterial: migrate setIsTransparent/setBlendMode/setRenderFace/_setAlphaCutoff
  from renderState API to shaderData.setInt()
- PBRMaterial: migrate renderQueueType to shaderData.setInt()
- SkyBoxMaterial/SkyProceduralMaterial: remove TS render state code (now in shader)
- Fix precompile script: handle undefined throw, non-fatal exit on failure
- Add precompile dedup flag to avoid repeated runs in b:all
- Regenerate all .gsp files to reflect shader source changes
- Move shaderLab configuration back to _initialize() where it was on dev/2.0
- registerShaders() stays in constructor (precompiled path doesn't need ShaderLab)
- PBR: material_OcclusionTextureCoord Float → Enum(UV0/UV1),
  material_AttenuationDistance range max 1 → 5
- PBRSpecular: same OcclusionTextureCoord fix, add Clear Coat group
  (5 properties matching PBR)
- BlinnPhong: material_EmissiveColor Color → HDRColor,
  material_Shininess range (1,1024) → (0,100)
- Particle: add full Editor block (Base, Emissive, Common)
- 2D/Trail: add full Editor block (same as Particle)
- Sky/Skybox: add Editor block (TintColor, Exposure, Rotation, CubeTexture)
- Sky/SkyProcedural: add Editor block (Exposure, SunMode, SunSize, etc.)
- Fix precompile script error logging to print full stack trace
- Add libs/ to tsconfig include so tsc generates types for .gsp imports
- Update package.json types paths to types/src/ (tsc output structure)
…omment handling

- Add ShaderLabUtils.skipComment() as shared primitive for comment detection
- Add ShaderLabUtils.removeComments() to strip all comments preserving newlines
- Preprocessor.parse() now strips comments before include expansion and macro collection,
  fixing false matches on #include inside block comments (e.g. FXAA3_11.glsl)
- BaseLexer.skipCommentsAndSpace() reuses ShaderLabUtils.skipComment()
- Lexer overrides skipCommentsAndSpace() to only skip whitespace since
  comments are already stripped by Preprocessor
- IShaderLab._parseShaderPass return type: IShaderProgramSource | undefined
…d unify export names

- precompile-shaders.mjs: auto-generate libs/index.ts from actual .gsp files
  on disk (variable names derived from src/Shaders/index.ts); add --index-only
  mode; add cleanOrphanedGsp to remove stale .gsp files; detect stale
  shader-lab dist and rebuild automatically
- rollup-plugin-shaderlab: remove buildStart for non-watch builds (precompile
  runs as npm script before rollup); in watch mode: .shader changes precompile
  immediately in watchChange, shader-lab/glsl changes defer to writeBundle;
  syncGsp deletes gsp and updates index when .shader is deleted
- package.json: add precompile script; b:module/b:umd/b:bundled/b:all run
  precompile before rollup; watch/dev enable PRECOMPILE=true
- src/index.ts: simplify to export * from ../libs
- Unify export names to {FileName}Source convention:
  * UberShaderSource → UberSource
  * FinalSRGBShaderSource → FinalSRGBSource
  * FinalAntiAliasingShaderSource → FinalAntiAliasingSource
  * BloomShaderSource → BloomSource
  * SAOShaderSource → ScalableAmbientOcclusionSource
- Fix BlinnPhong shadow: SCENE_IS_CALCULATE_SHADOWS
  → NEED_CALCULATE_SHADOWS in MobileBlinnPhong.glsl
- Fix BlinnPhong tangent: use renderer_NormalMat
  instead of renderer_ModelMat
- Restore MATERIAL_OMIT_NORMAL guard in VertexPBR
  and ForwardPassBlinnPhong
- Fix project-loader.ts: restore ShaderLab instance
- Add const RenderState to UIDefault.shader and
  remove TS render state code from ui/index.ts
- ShaderPool: throw on registration failure instead
  of swallowing with Logger.warn
GuoLei1990 added 4 commits May 1, 2026 20:58
…if shadowing

Under ShaderLab's lazy preprocessor the same name can be a macro in
one preprocessor arm and a variable in the mutually-exclusive arm
(FXAA-style cross-arm shadowing):

  #if FXAA_GATHER4_ALPHA == 1
    #define lumaS 1.0
  #else
    float lumaS = 0.5;
  #endif
  ...
  use(lumaS);

Grammar `single_declaration -> fully_specified_type MACRO_CALL [= initializer]`
(added in 87cb2b5) accepts the parse, but `MacroCallSymbol.referenceSymbolNames`
collected only the macro value's referenced names, not the macro name itself.
Use sites of `lumaS` are also tagged MACRO_CALL, so `referenceGlobal` was never
called for `lumaS` -- the global-decl emitter then dropped the sibling-arm
`float lumaS = 0.5;` and the variant where the sibling preprocessor condition
was false had `lumaS` undeclared.

Fix `VariableIdentifier.semanticAnalyze` to also probe the macro name itself
when the use site is MACRO_CALL, with a one-name `macroDefineList`
short-circuit bypass so the symbol-table lookup actually reaches the
sibling-arm declaration.

Tests:
- New fixture `cross-if-declarator-collision.shader` reproduces the FXAA
  pattern; test asserts the emitted vertex retains `float lumaS = ...` to
  lock both halves of the fix (grammar + codegen).
- Drive-by: clear stale "long-standing limitation" comment in
  `type-alias-repro.shader` (the limitation was lifted in 87cb2b5;
  `macro-type-alias.shader` now covers macro-as-type usage).

Verified: 35/36 test fixtures pass (the 1 failure is `render-state.shader`,
already failing on baseline, unrelated), 22/22 built-in shaders pass.
Replace the catch-all `Utility/` and `AO/` buckets with semantic
directories that mirror the runtime's role:

  Shaders/AO/                        -> Shaders/Lighting/
  Shaders/Utility/ShadowMap.shader   -> Shaders/Pipeline/ShadowCaster.shader
  Shaders/Utility/DepthOnly.shader   -> Shaders/Pipeline/DepthOnly.shader
  Shaders/Utility/Blit.shader        -> Shaders/Blit/Blit.shader
  Shaders/Utility/BlitScreen.shader  -> Shaders/Blit/BlitScreen.shader

Shader names follow the directory:

  AO/ScalableAmbientOcclusion -> Lighting/ScalableAmbientOcclusion
  Utility/ShadowMap           -> Pipeline/ShadowCaster
  Utility/DepthOnly           -> Pipeline/DepthOnly
  Utility/Blit                -> Blit/Blit
  Utility/BlitScreen          -> Blit/BlitScreen

Rationale:
- `Lighting/` mirrors core/src/lighting/ -- SSAO is a screen-space
  lighting effect, not a post-process effect (matches Unity URP/HDRP
  and Unreal classification).
- `Pipeline/` holds the shared base passes that material shaders
  reference via UsePass -- distinct from `Blit/` which holds the
  Blitter utility's internal shaders. Both were lumped into
  `Utility/` despite serving different roles.
- `AO/` was using a non-spell-out abbreviation while core uses the
  full `ambientOcclusion`; with only one AO shader today, flattening
  to `Lighting/ScalableAmbientOcclusion` avoids redundant
  `AmbientOcclusion/ScalableAmbientOcclusion` until a second AO
  algorithm appears.

ShaderLibrary chunks unscatter from Common/ to their semantic homes:

  ShaderLibrary/AO/                       -> ShaderLibrary/Lighting/
  ShaderLibrary/Common/MobileBlinnPhong   -> ShaderLibrary/BlinnPhong/
  ShaderLibrary/Common/BlitVertex         -> ShaderLibrary/Blit/

`Common/` now only holds genuinely shared utilities (Common, Color,
Fog, Light, Normal, Transform, Attributes, UV, Position*,
ViewDirection, WorldPosition). MobileBlinnPhong was only consumed by
BlinnPhong; BlitVertex is the vertex implementation for Blit-family
shaders.

Drive-by: remove stale packages/shader/types/compiled/ and
packages/shader/types/shaders/ directories left over from a
pre-rename build.

Updated: PBR/BlinnPhong/Unlit UsePass references, ShaderPool source
imports & registration order comment, BasicResources.ts Shader.find
calls, ScalableAmbientObscurancePass.SHADER_NAME, Shader.test.ts
UsePass, PrecompileBenchmark.test.ts builtinSource paths and
Shader.find.

Verified: 35/36 test fixtures pass (the 1 failure is
render-state.shader, already failing on baseline, unrelated),
22/22 built-in shaders compile.
…ia DI

The bundler used to obtain its include map indirectly: importing
`@galacean/engine` triggered `ShaderPool.init()` (an Engine.ts top-level
side effect) which registered engine-shader's dist snapshot into
`ShaderFactory._includeMap`. Preprocessor then read that map via a hard
import. This created a build-pipeline cycle:

  precompile -> consumes engine-shader's dist -> rebuilt by precompile

Renaming a chunk path (e.g. `ShaderLibrary/AO/X.glsl` ->
`ShaderLibrary/Lighting/X.glsl`) required rebuilding engine-shader before
precompile could see the new path, otherwise precompile failed against the
stale snapshot.

Replace the implicit global with explicit DI:

- `Preprocessor.parse(source, basePath, includeMap)` — pure function over
  the injected map; no `ShaderFactory` import.
- `ShaderCompiler` carries a `_includeMap` instance field (defaults to `{}`)
  and forwards it to `Preprocessor`. Public `new ShaderCompiler()` signature
  unchanged.
- Runtime side: `Engine._initialize` binds
  `shaderCompiler._includeMap = ShaderFactory._includeMap` once when the
  user-supplied compiler is registered, so `Shader.create` keeps querying
  the live engine registry — runtime behaviour identical.
- Build-time side: bundler builds its own map by scanning src
  (`<inputDir>/*.glsl` + sibling `<ShaderLibrary>/*.glsl` by convention)
  and injects it into `_includeMap`. Bundler no longer imports
  `@galacean/engine`; it inlines `_shaderRootPath` as a literal.

Net effect: build artifacts no longer participate in their own build, the
chunk-rename scenario works on the very first precompile pass, and the
preprocessor is a pure function. No public-API changes.

Drive-by: trim verbose JSDoc in bundler/precompile.ts (310 -> 245 lines).
…aderCompiler

After ea2d000 decoupled the preprocessor's include map via DI
(ShaderCompiler holds a `_includeMap` field defaulting to `{}`), tests
that bypass `WebGLEngine.create({ shaderCompiler })` and assign
`Shader._shaderCompiler` directly leave the map empty -- every `#include`
lookup then fails.

Inject the runtime map (`ShaderFactory._includeMap`) right before
assigning `Shader._shaderCompiler` in each affected test setup. Production
code stays unchanged: binding is the consumer's responsibility, not
shader-compiler's nor engine's.

Verified: 1330/1330 tests pass.
@GuoLei1990 GuoLei1990 force-pushed the refactor/glsl-to-shaderlab branch from 632de77 to bd50c31 Compare May 1, 2026 14:53
GuoLei1990 added 7 commits May 1, 2026 23:17
…methods

ShaderFactory was the only meaningful resident of `shaderlib/`. Move it
under `shader/` next to its consumers (ShaderPool/ShaderPass/Shader),
delete the now-empty `shaderlib/` directory, and tag the class
`@internal` (`stripInternal: true` removes it from the public d.ts).

Also drop dead and over-wrapped surface:
- delete unused `getInclude` / `unRegisterInclude` / `parseIncludes`
  (Preprocessor switched to dependency-injected map, no callers left)
- drop the unused Logger import that came with `parseIncludes`
- inline `_has300Output` (1-call wrapper around a regex test)
- rename internal-only `_includeMap` / `_shaderExtension` -> `includeMap`
  / `shaderExtension` (class is fully `@internal`; underscore prefix
  added no info)

Updated callers: Engine.ts, ShaderPool.ts, ShaderPass.ts plus four test
files; shader-compiler doc comment refreshed.

Verified: npm run b:module clean, 1330/1330 tests pass.
…used work

Replace the over-permissive `RENDERER_HAS_TANGENT`-only gating with two
narrower macros that match what the surface actually consumes:

- `NEED_VERTEX_TANGENT` — vertex stage reads mesh tangent and writes the
  tangentWS / bitangentWS varying. Requires both a mesh tangent attribute
  AND a tangent-space normal map (base or clear coat). Anisotropy alone
  falls back to dFdx/dFdy in fragment, so it does not pull tangent
  through the vertex pipeline.
- `NEED_TANGENT_SPACE` — fragment stage builds a tangent space (T/B
  vectors on SurfaceData + a temporary `mat3 tbn`). Triggered by any
  tangent-space material feature: normal map, clear coat normal map, or
  anisotropy. With NEED_VERTEX_TANGENT the basis comes from the
  interpolated varying; otherwise it is derived from screen-space
  derivatives.

Previously a model carrying tangent attributes always paid for tangent
skin/blend-shape transform, world-space projection, and two extra vec3
varyings — even when no normal map / anisotropy was bound. With this
change those costs are skipped in the common "GLTF mesh + plain PBR"
path. Matches the gating philosophy of dev/2.0's pbr_helper.glsl, Unity
URP's REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR, and Filament's
HAS_TANGENT_SPACE.

The two macros are defined at the top of ForwardPassPBR.glsl /
ForwardPassBlinnPhong.glsl (the entry chunks for each shader family) so
all downstream sub-chunks see the resolved values without needing a
separate defines file.
- shader-mrt.ts: update the inline custom shader's `UsePass` from the
  obsolete `Utility/ShadowMap/Default/ShadowCaster` to the current
  `Pipeline/ShadowCaster/Default/ShadowCaster`. The old path stopped
  resolving after the shader directory reorganization, leaving the
  scene with no valid material so the canvas never rendered and
  Playwright timed out waiting for the screenshot download event.
- config.ts: drop the residual diff tolerance for
  particleRenderer-shape-transform now that the regenerated baseline
  matches output exactly.
- fixtures/originImage/Particle_particleRenderer-shape-transform.jpg:
  refresh the baseline image to match current renderer output.
…tity activation

`rootEntity.createChild()` activates the entity immediately, so
`addComponent(ParticleRenderer)` triggers _onEnable → generator.play()
before the next-line `useAutoRandomSeed = false` assignment runs. play()
then takes the default-true branch and seeds the generator with
`Math.random()`, producing a fresh screenshot every run.

Construct the entity detached, configure useAutoRandomSeed and the
emission shape, then `addChild` to activate. play() now sees
useAutoRandomSeed=false and skips the random seed roll, making the
particle emission stream deterministic across runs.

Refresh the baseline image to match the now-stable output.
- Remove unused legacy Common chunks (Color/Position/PositionClipSpace/
  UV/WorldPosition). They were carried over from the old chunk-style
  shader pipeline and lost all consumers after the ShaderLab function
  rewrite — every defined function had zero call sites.
- Inline the 4-line `getViewDirection` into Common.glsl and remove
  ViewDirection.glsl. Single consumer (BlinnPhong) and trivial body
  did not justify a standalone chunk file.
- Move Light.glsl from Common/ into Lighting/. Its semantic class
  (light source struct + scene uniforms + cull helpers) belongs with
  the lighting effects rather than with low-level data scaffolding.
- Group AO chunks into Lighting/AmbientOcclusion/ subdirectory now that
  Lighting/ contains both basic light declarations and SAO-specific
  helpers; the longer subdir name reads better than the bare "AO"
  abbreviation.
- Update all `#include` paths in PBR / BlinnPhong / SAO shaders and
  regenerate the auto-generated ShaderLibrary/index.ts (58 chunks,
  down from 64).
The previous `.gsp` extension carried Galacean-internal branding
without conveying intent. Rename to `.shaderc` for two reasons:

1. Mirror Python's `.py → .pyc` convention (source extension + `c`
   marker for "compiled"): visually adjacent to the source, suffix
   blood-relationship is obvious, and the `c` is recognized in DX
   land too (`.cso` = Compiled Shader Object — same `c=compiled`
   semantics).
2. Drop the `g` prefix that only made sense inside Galacean. The
   artifact is a runtime-ready shader bundle compiled from .shader
   sources; "shaderc" is descriptive without project-specific noise.

Also fix a long-standing bug in the loader registration: the
@resourceLoader decorator listed only `["shader"]`, so the
ResourceManager could never route precompile artifacts to
ShaderLoader. Add `"shaderc"` so URLs ending in `.shaderc` reach
the precompiled-loading branch.

Renamed:
- 22 precompile artifacts under packages/shader/compiledShaders/
- `gspPathToVarName` → `shadercPathToVarName`
- `cleanOrphanedGsp` → `cleanOrphanedBundles`
- `removeGspFor` → `removeBundleFor`
- Local var `gspRelative` / `gspPath` → `bundleRelative` / `bundlePath`

Updated extension references:
- packages/shader/src/global.d.ts (module declaration)
- packages/shader-compiler/src/bundler/{transform,precompile,index,utils}.ts
- packages/loader/src/ShaderLoader.ts
- packages/core/src/shader/ShaderPool.ts (comment)
- tests/src/shader-compiler/Precompile{ABTest,Benchmark}.test.ts
- packages/shader/compiledShaders/index.ts (auto-regenerated)
The shader-compiler used to import Logger / Color / ObjectPool / enums from
@galacean/engine, which made it depend on the entire engine umbrella at
runtime. That created a build-time cycle: precompile loads shader-compiler/
dist, which require()s engine-core, but engine-core's dist depends on the
.shaderc bundles produced by precompile. The PR worked around it with
SKIP_GALACEAN, fs.existsSync guards, a graceful tryLoadShaderCompiler
fallback, and effectively required two b:all runs on a cold checkout.

Cut the cycle at the source: shader-compiler no longer imports anything
from @galacean/engine. Only @galacean/engine-math (for Color) remains, as
a single runtime dep — math is a leaf with no further engine dependencies,
so the workspace dependency graph becomes a clean DAG.

What moves into shader-compiler:
- src/enums/  — local copies of the 9 .shaderc wire-format enums
                (BlendFactor, BlendOperation, ColorWriteMask, CompareFunction,
                 CullMode, RenderQueueType, RenderStateElementKey,
                 StencilOperation, ShaderLanguage), kept in lockstep with
                the engine-core copies via README convention. Industry-
                standard for offline shader compilers (Unity, Unreal,
                glslang all do this).
- src/common/ObjectPool.ts — local ClearableObjectPool / ReturnableObjectPool /
                IPoolElement implementations.
- Inlined SHADER_ROOT_PATH constant in Preprocessor.ts (was
                ShaderPass._shaderRootPath).
- console.error/warn at error/warning sites (was a noop-by-default Logger
                that silently swallowed errors).

Build-side changes:
- shader-compiler/package.json: drop @galacean/engine deps and peerDependencies,
                add @galacean/engine-math as the only runtime dep, add
                umd.globals mapping math to Galacean (so UMD doesn't inline
                math, ~33KB minified savings on browser.min.js).
- shader-compiler/rollup.config.js: runtimeExternal=[] in b:compiler so math
                gets inlined into the self-contained dist used by precompile;
                resolve mainFields=["debug","module","main"] lets the math
                source resolve via its `debug` field on a fresh checkout
                where math/dist doesn't exist yet.
- bundler/precompile.ts: drop the graceful skip + window/document polyfill
                — neither is needed once shader-compiler is engine-free.

Build pipeline collapses back to a single rollup pass, structurally
identical to dev/2.0 plus a precompile step in front. SKIP_GALACEAN /
fs.existsSync / tryLoadShaderCompiler / two-pass orchestration are all
gone. Cold-boot `pnpm b:all` succeeds in one run.

Bundle size impact vs the previous PR head: zero on every consuming package
(core, math, loader, rhi-webgl, shader, galacean, physics-*, ui, xr*).
shader-compiler itself grows ~16KB (the inlined enums + ObjectPool).
GuoLei1990

This comment was marked as outdated.

GuoLei1990 added 6 commits May 2, 2026 14:46
…nd clarify intent

- mainFields=["debug"] (was ["debug", "module", "main"]): the runtime
  entry now resolves workspace deps strictly to source via the `debug`
  field, removing the dist-fallback path that could never legitimately fire
  (math always has a `debug` field) but invited stale-dist surprises if it
  ever did.
- Reword the header / runtimeExternal / swcPluginRuntime comments to spell
  out exactly which build this is, what it produces, and why nothing is
  externalized at the runtime entry. No behavior change beyond the
  mainFields tightening.
…Info) to galacean umbrella

The umbrella `@galacean/engine` package now owns the three top-level side
effects that previously lived in `engine-core`:

  - `Polyfill.registerPolyfill()`  (matchAll / AudioContext / TextMetrics
                                     / Promise.finally — touches `window`)
  - `SystemInfo._initialize()`      (browser platform detection)
  - `ShaderPool.init()` + `registerShaders()` (built-in shader assets)

Why
---
`engine-core` is supposed to be a generic engine runtime — neutral about
which shader set ships in the box and which environment it runs in. With
those three top-level effects sitting in `core/src/index.ts` (and
`core/src/Engine.ts`), any consumer that only imports an enum or utility
from core was forced to drag the entire flavor closure (engine-shader,
window touches) along with it. That ruled out two things:

  - shader-compiler couldn't import `engine-core` enums / pools — even via
    rollup's tree-shake — because `Engine.ts`'s top-level `ShaderPool.init()`
    pulled in engine-shader, whose `export * from "../compiledShaders"` is a
    physical file that doesn't exist yet at precompile time. We had been
    working around this by maintaining local copies of nine enums plus
    `ObjectPool` inside shader-compiler.
  - core could never advertise `"sideEffects": false`, so user-app bundles
    couldn't tree-shake unused core modules.

What moves
----------
- `core/src/shader/ShaderPool.ts` → `galacean/src/ShaderPool.ts`
  Plus drop the cached `particleFeedbackPass` static field — particle code
  now does `Shader.find("Effect/ParticleFeedback")` directly.
- `core/src/Engine.ts`: drop the top-level `ShaderPool.init()` and the
  in-constructor `ShaderPool.registerShaders()` call.
- `core/src/index.ts`: drop the top-level `Polyfill.registerPolyfill()`,
  re-export `Polyfill` instead so the umbrella can call it.
- `core/src/SystemInfo.ts`: drop the trailing `SystemInfo._initialize()`.
- `core/src/particle/ParticleTransformFeedbackSimulator.ts`: look up the
  feedback shader via `Shader.find` with an explicit error when the
  umbrella hasn't registered it (which would only happen if the consumer
  built a custom flavor without registering built-in shaders).
- `core/package.json`: drop the `@galacean/engine-shader` dependency.

- `galacean/src/index.ts`: at module load, call `Polyfill.registerPolyfill()`
  → `SystemInfo._initialize()` → `ShaderPool.init()` →
  `ShaderPool.registerShaders()` (the order matters — polyfills first,
  platform detection next, then `#include` map and shader registration
  before any `Material` constructor's `Shader.find` runs).
- `galacean/package.json`: pick up the new `@galacean/engine-shader`
  dependency that core dropped.
- `galacean/src/ShaderPool.ts`: same shape as the old core copy minus the
  `particleFeedbackPass` cache.

Drop the local enum / pool copies in shader-compiler
----------------------------------------------------
With core no longer flavor-bound, shader-compiler can finally import the
shared enums and pools from `@galacean/engine-core` without the closure
expansion that previously hit the missing `compiledShaders/index.ts`. So:

- Delete `shader-compiler/src/enums/` (nine wire-format enum copies + a
  README describing the sync convention) and `shader-compiler/src/common/
  ObjectPool.ts`.
- Rewrite the seven import sites to pull from `@galacean/engine-core`.
- Add `@galacean/engine-core` to `shader-compiler/package.json` deps and
  to the UMD `globals` map so `browser.min.js` doesn't inline core.

Verification
------------
- Cold-boot `pnpm b:all` succeeds in a single pass; no `.shaderc` files
  needed up front, no skipped precompile, no fallback paths.
- shader-compiler dist contains zero inline ShaderPool / Engine / Polyfill
  / SystemInfo / MathUtil / GLSL chunk markers; the only `require` calls
  on the `@galacean/*` namespace are math and core.
- Total dist bytes across all packages: 56,119,531 → 55,907,479
  (-212,052 bytes, ≈ -207 KB). The shader-compiler subtree shrinks ~263 KB
  thanks to nine enum + pool copies no longer being inlined into 16 output
  artifacts (8 .js + 8 .map across release / verbose × CJS / ESM / UMD /
  minified). core shrinks ~4 KB; galacean grows ~21 KB to host the
  bootstrap. Eight unrelated packages are byte-identical.
- vitest suite passes (manually verified).

Note: this still doesn't set `"sideEffects": false` on core — four
animation curve assemblers still rely on bare `import "./..."` to
self-register. Switching them to explicit `registerAssembler(...)` calls
would unlock the flag and let user bundles tree-shake unused core modules.
That's a follow-up; the present change keeps the assemblers as-is so the
behavior surface stays identical.
…r code

Until now the test suite mostly imported `WebGLEngine` from
`@galacean/engine-rhi-webgl` directly. That worked only because of an
accident: `@galacean/engine-core` had three top-level side effects
(`ShaderPool.init()`, `Polyfill.registerPolyfill()`,
`SystemInfo._initialize()`) that fired on any core import, no matter how
indirect — so the tests got built-in shaders, polyfills, and platform
detection wired up "for free" even though they never touched the umbrella
package the way real consumers do.

The previous commit moved those three bootstraps from `engine-core` into
the `@galacean/engine` umbrella so core can be flavor-agnostic. That
exposes the test path mismatch: 75 test files import `WebGLEngine` from
`engine-rhi-webgl`, never load the umbrella, and now have no built-in
shaders → `BasicResources`'s `Shader.find("Blit/Blit")` returns null →
`new WebGLEngine` fails with `Cannot read properties of undefined`.

Fix by routing every test's `WebGLEngine` (and the three Polyfill tests'
dynamic `import("@galacean/engine-core")`) through the umbrella, which is
the same import users write in real applications. No test logic changes —
77 files, only the import sources move:

  -import { WebGLEngine } from "@galacean/engine-rhi-webgl"
  +import { WebGLEngine } from "@galacean/engine"

`WebGLEngine` is the same class either way (the umbrella re-exports
rhi-webgl's), so the tests behave identically — they just exercise the
real consumer entry point and pick up the umbrella's bootstrap as a
side effect of the import they were already doing.
PBR shader source rewrite (raw GLSL → ShaderLab) produces a 4-pixel
±1 LSB diff against the dev/2.0 baseline on this case. Math is
equivalent; the drift comes from GLSL compiler optimization paths
shifting under the new shader text. Set diffPercentage to 4/960000
≈ 0.000417 so the case passes without masking real regressions.
After d21484b dropped material.renderState, no public API hands users
a mutable RenderState/BlendState/DepthState/RasterState/StencilState
instance. The classes are still imported by engine internals via relative
paths, so removing them from the package barrel does not change behavior
— only stops surfacing internal-only types as public API. Keep enums
(BlendFactor, CullMode, ...) public since they are passed to shaderData
when overriding render state from material code.
… barrel removal

The white-box RenderState tests construct BlendState/DepthState/RasterState/
StencilState directly. After 1e1b99c removed these classes from the public
barrel, the existing `import { BlendState, ... } from "@galacean/engine-core"`
broke at module load time. Switch to the same internal-source import pattern
already used by tests/src/shader-compiler/* and tests/src/loader/*.
GuoLei1990

This comment was marked as outdated.

zhuxudong and others added 8 commits May 7, 2026 15:34
pnpm clean wipes shader-compiler/dist, but the root rollup.config.js
imports `@galacean/engine-shader-compiler/bundler` at config-load time,
so `pnpm watch` and `pnpm dev` failed with module-not-found until a
manual precompile was run. Both scripts now invoke `npm run precompile`
first, matching the b:module / b:umd / b:bundled / b:all flow.

Also extract `b:compiler` so the compiler can be rebuilt in isolation
without re-running the full precompile.
The static `_repeatIncludeSet` accumulated paths across consecutive
shader compiles, so the second shader to include any shared chunk
triggered a "multiple times" warning even when each shader only
included it once. Clear the set at every top-level `parse()` so the
warning fires only on intra-shader repeats. The recursive expansion
now goes through `_parseInternal` to keep the per-shader scope intact.
The ShaderLab parser requires every variable used on the right-hand
side of `RenderQueueType = <name>;` to be declared first via
`RenderQueueType <name>;`, mirroring how PBR / Unlit / BlinnPhong
declare `RenderQueueType renderQueueType;` before the assignment.

DepthOnly and ShadowCaster were assigning to `material_*RenderQueue`
without declaring those names, so precompile failed with
"Invalid RenderQueueType variable: ...". Add the missing declarations.
Third-party shaders (e.g. FXAA3_11.glsl) commonly wrap an `#include`
directive inside a `/* ... */` documentation block. The include regex
ran on the raw source, so those documentation lines were treated as
live includes and reported "not founded" because the referenced file
never existed.

Use regex alternation: a block-comment branch fires first and consumes
the entire `/* ... */` segment (and any directives inside) before the
include branch gets a chance, so embedded `#include` literals never
reach the substitution callback. The callback distinguishes branches
by checking whether the include capture group is set; matched comments
fall through unchanged. Source text is not mutated — comments stay in
place and downstream error messages still see the original lines.

Single-line `//` comments are already excluded by the existing
`^[ \\t]*#include` anchor.
d21484b dropped Material's renderState getter but left the loader-side
parsing intact, so `parseProperty(material, "renderState", ...)` would
recurse into `material["renderState"]` (now undefined) and crash on any
.mat schema carrying a renderState block.

Remove the orphan path entirely:
- IRenderState interface and schema field in MaterialSchema.ts
- renderState destructure and parseProperty call in MaterialLoader.ts
- parseProperty helper itself (only call site was the renderState wiring)

Old .mat files with a renderState block are silently ignored — render
state now flows exclusively through ShaderLab DSL + shaderData, matching
the d21484b design.
`startWatcher` already re-collects `_includeMap` when a `.glsl` file
changes, but the static `Preprocessor._chunkOutputCache` still held the
pre-edit expansions, so the next `.shader` recompile fed downstream the
stale chunk output.

Add a watch-mode-only `_clearChunkCache` hook on the Preprocessor and
forward it through ShaderCompiler. The handler invokes it alongside the
include-map refresh so the cache and the source map stay in lockstep.
The hot path stays a path-keyed lookup with no string comparisons.
Move _chunkOutputCache from Preprocessor static onto ShaderCompiler as a
private instance field that lives next to _includeMap (the data it derives
from). Replace the public _includeMap field with a private one + an explicit
_setIncludeMap() method that swaps the map and clears the derived cache in
one step. Concurrent ShaderCompiler instances (editor multi-preview, parallel
tests, SSR) can no longer poison each other's caches, and the bundler watcher
no longer needs a separate clear hook.

Drop the repeat-include warning entirely. It only fired on diamond
dependencies inside library chunks (e.g. ParticleFeedback's NoiseCommon,
Uber's Tonescale) — situations the shader author can't fix and that the
chunk cache already deduplicates at zero cost. With the warning gone, the
repeat tracking set, _parseInternal hop, and Preprocessor's residual static
state all disappear; Preprocessor becomes a stateless `#include` expander.

- packages/core/src/Engine.ts: switch to shaderCompiler._setIncludeMap(...)
- packages/shader-compiler/src/ShaderCompiler.ts:
  - drop _clearChunkCache; add _setIncludeMap with cache-clear side effect
  - move _chunkOutputCache here as a per-instance Map
- packages/shader-compiler/src/Preprocessor.ts:
  - drop static _chunkOutputCache, _repeatIncludeSet, _clearChunkCache
  - drop _parseInternal hop and the multiple-include warning
  - parse(source, basePath, includeMap, chunkOutputCache) is now pure
- packages/shader-compiler/src/bundler/precompile.ts:
  - replace _clearChunkCache + _includeMap-assign with _setIncludeMap call
- Merge bundler/transform.ts and bundler/utils.ts into rollup.ts and
  precompile.ts (no external consumers; pure transform inlined).
- Split the rollup plugin into bundler/rollup.ts as the single adapter
  with explicit subpath export `./bundler/rollup`; drop the aggregate
  `bundler/index.ts` barrel.
- Add `--emit-sources` CLI flag generating raw-source indexes
  (`<input>/index.ts` for .shader, sibling `ShaderLibrary/index.ts`
  for .glsl). Replaces engine-shader's local
  generate-source-indexes.js script.
- collectIncludeMap resolves the standard ShaderLibrary chunks via the
  `@galacean/engine-shader/sources` release entry, looked up from
  inputDir's node_modules so toolkit/editor consumers find it. Falls
  back to sibling source dir during engine self-build cold-start.
- Update root engine and engine-toolkit rollup configs to import from
  the new `./bundler/rollup` subpath.
- Fix .shaderc index header bin name to actual `shader-compiler-precompile`.
GuoLei1990

This comment was marked as outdated.

Copy link
Copy Markdown
Member

@GuoLei1990 GuoLei1990 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已关闭问题清单

上轮(2026-05-08)提出的问题:

问题 状态
[P1] _resolveUsePass 静默返回 undefined 未修复(见下文)
[P2] collectIncludeMap catch 块静默吞异常 未修复

自上次审查(2026-05-08 13:41:58Z,commit fbeb3fb7fe)至今,PR 无新 commit,上述问题均未处理。


问题

[P1] Shader.ts _resolveUsePass — 目标 shader/subShader/pass 不存在时仍静默返回 undefined

当前实现:

private static _resolveUsePass(passName: string): ShaderPass | undefined {
  // ... 格式解析 ...
  return Shader.find(shaderName)
    ?.subShaders.find((s) => s.name === subShaderName)
    ?.passes.find((p) => p.name === passNamePart);
}

格式校验已修复,但当 Shader.find(shaderName) 返回 null(shader 未注册)或 subShader/pass 名不匹配时,链式 ?. 返回 undefined,被推入 passList。调用方未做非 null 检查,undefined 在后续 shaderPass._renderStateshaderPass._getShaderProgram 时崩溃,错误信息完全不可读(NullPointerError 而非配置错误提示)。

UsePass 是编译期引用,找不到就是配置错误,应在 _resolveUsePass 内立即抛出清晰错误:

private static _resolveUsePass(passName: string): ShaderPass {
  // ... 格式解析 ...
  const shader = Shader.find(shaderName);
  if (!shader) throw new Error(`UsePass "${passName}": shader "${shaderName}" not found. Ensure it is registered before this shader.`);
  const subShader = shader.subShaders.find((s) => s.name === subShaderName);
  if (!subShader) throw new Error(`UsePass "${passName}": subShader "${subShaderName}" not found in shader "${shaderName}".`);
  const pass = subShader.passes.find((p) => p.name === passNamePart);
  if (!pass) throw new Error(`UsePass "${passName}": pass "${passNamePart}" not found in "${shaderName}/${subShaderName}".`);
  return pass;
}

返回值类型也应改为 ShaderPass(去掉 | undefined),调用方可以相应简化。

[P2] collectIncludeMap 的 catch 块静默吞掉 createRequire 失败

try {
  const requireFromInput = createRequire(path.join(inputDir, "package.json"));
  // ...
} catch {
  // 静默回退
}

inputDir 不是 npm 包目录时(无 package.json),createRequire 抛异常,被 catch {} 吞掉,静默回退到 sibling source 解析。回退逻辑本身是合理的,但建议加一行 debug log,便于在冷启动失败时排查:

} catch (e) {
  Logger.debug(`collectIncludeMap: createRequire failed for "${inputDir}", falling back: ${e}`);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation shader Shader related functions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants